package cn.changeforyou.demo.netty.server;

import cn.changeforyou.demo.netty.common.Data;
import cn.changeforyou.demo.netty.common.Data.MsgType;
import cn.changeforyou.demo.netty.common.Req;
import cn.changeforyou.demo.netty.common.Req.ReqType;
import cn.changeforyou.demo.netty.common.Res;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelProgressivePromise;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 生命周期请看 https://blog.csdn.net/u014131617/article/details/86476522
 *
 * @author zhyu
 */
@Sharable
public class ServerHandler extends ChannelInboundHandlerAdapter {

	Logger log = LoggerFactory.getLogger(ServerHandler.class);
	AttributeKey<String> sessionKey = AttributeKey.valueOf("session");
	Map<String, Channel> id2ChannelMap = new ConcurrentHashMap<>();
	Map<Channel, String> channel2IdMap = new ConcurrentHashMap<>();
	Map<String, Integer> id2StatisticsMap = new ConcurrentHashMap<>();

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) {
		Data data = (Data) msg;
		Channel channel = ctx.channel();

		Attribute<String> attr = channel.attr(sessionKey);
		String session = attr.get();
		log.info("用户:{}, channel:{}, ip:{}", session, channel.hashCode(), ((InetSocketAddress) channel.remoteAddress()).getAddress());
		if (data.getMsgType() == MsgType.req) {
			Req req = (Req) data.getContent();
			if (req.getReqType() == ReqType.login) {
				String token = ((String) req.getData());
				attr.set(token);
				id2ChannelMap.put(token, channel);
				channel2IdMap.put(channel, token);
				log.info("收到登录信息, 开始响应 ip:{}, id:{}, channel:{}", ((InetSocketAddress) channel.remoteAddress()).getAddress(), channel.hashCode());
				channel.writeAndFlush(Data.instanceResponse(new Res(0, "ok", null, req.getMsgId())));
			}
		}
	}

	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		if (evt instanceof IdleStateEvent) {
			IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
			if (idleStateEvent.state() == IdleState.READER_IDLE) {
				Channel channel = ctx.channel();
				log.info("检测到用户[{}]心跳超时, 开始断线处理", getUserByChannel(channel));
				channel.close();
			}
		} else {
			super.userEventTriggered(ctx, evt);
		}
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		cause.printStackTrace();
		removeChannelInMap(ctx, "exception");
		ctx.channel().close();
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		ctx.fireChannelInactive();
		String event = "inactive";
		removeChannelInMap(ctx, event);
	}



	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		ctx.fireChannelInactive();
		Attribute<String> attr = ctx.channel().attr(sessionKey);
		String session = attr.get();
		log.info("channel active, {}", session);
	}

	@Override
	public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
		ctx.fireChannelRegistered();
		Attribute<String> attr = ctx.channel().attr(sessionKey);
		String session = attr.get();
		log.info("channel registered, {}", session);
	}

	@Override
	public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
		ctx.fireChannelUnregistered();
		Attribute<String> attr = ctx.channel().attr(sessionKey);
		String session = attr.get();
		log.info("channel Unregistered, {}", session);
	}

	String getUserByChannel(Channel channel) {
		if (channel2IdMap.containsKey(channel)) {
			return channel2IdMap.get(channel);
		}
		return null;
	}

	public void send(String user, Data data) {
		if (id2ChannelMap.containsKey(user)) {
			id2ChannelMap.get(user).writeAndFlush(data);
			log.info("发送信息:{}, 给用户: {}", data, user);
		}else {
			log.info("用户不存在 : {}", user);
		}
	}

	public void printStatistics() {
		log.info("统计数据: {}", id2StatisticsMap);
	}

	public void broadcast(Data data) {
		log.info("广播信息:{}, 用户数:{}", data, id2ChannelMap.size());
		for (Map.Entry<String, Channel> entry : id2ChannelMap.entrySet()) {
			Channel channel = entry.getValue();
			String user = entry.getKey();
			ChannelProgressivePromise promise = channel.newProgressivePromise();
			promise.addListener(f -> {
				if (!f.isSuccess()) {
					log.info("用户[{}]的信息没有被广播到位", user);
				} else {
					id2StatisticsMap.compute(user, (k, v) ->
					{
						if (null == v) {
							return 1;
						} else {
							return v + 1;
						}
					});
				}
			});
			channel.writeAndFlush(data, promise);
		}
	}

	private void removeChannelInMap(ChannelHandlerContext ctx, String event) {
		Attribute<String> attr = ctx.channel().attr(sessionKey);
		String session = attr.get();
		log.info("channel {}, {}", event, session);
		if(null != session) {
			Channel channel = id2ChannelMap.remove(session);
			channel2IdMap.remove(channel);
		}
	}

}
